home *** CD-ROM | disk | FTP | other *** search
/ PC World Interactive 7 / PC World Interactive 7.iso / program / cprog.EXE / DLINK.C < prev    next >
Text File  |  1990-06-26  |  25KB  |  1,133 lines

  1. #include <dos.h>
  2. #include <string.h>
  3. #include <stdio.h>
  4. #include <conio.h>
  5. #include <process.h>
  6. #include "keys.h"
  7.  
  8. /* structure template for list */
  9. struct address
  10. {
  11.   char FirstName[15];
  12.   char LastName[15];
  13.   char Street[40];
  14.   char City[20];
  15.   char State[3];
  16.   char Zip[10];
  17.   char Phone[15];
  18.   struct address *Next;           /* pointer to next record  */
  19.   struct address *Prior;          /* pointer to Prior record */
  20. };
  21.  
  22. struct address Dummy;          /* dummy record for data entry */
  23. struct address *FirstRecord;      /* pointer to first record */
  24. struct address *Last;             /* pointer to last record  */
  25.  
  26. /* constants for prompts */
  27. const char *FirstPrompt = "        enter first name:  ";
  28. const char *LastPrompt = "         enter last name:  ";
  29.  
  30. /* file name field */
  31. char FileName[15];
  32.  
  33. /* count of records */
  34. int count;
  35.  
  36. /* structure for screen setup */
  37. struct text_info textinfo;
  38.  
  39. /* function prototypes */
  40. void main_menu(), enter(), search(), save(), load(), charbox(), Beep(void);
  41. void inputs(char *s, int count), cls(), go_xy(), display_screen();
  42. void put_char(char a), pspace(int count), labels(void), browse(void);
  43. void square(int x, int y), done(int selection), WriteFile();
  44. void go_xy(int x, int y), put_char(char a), open_file();
  45. void field(char *field, int max);
  46. void edit(void), spawnit(void);
  47. int getkey(), menu_select();
  48. unsigned edit_field(const unsigned row, unsigned column,
  49.             char *s, unsigned max, unsigned back, unsigned fore);
  50. unsigned display(struct address *info, char *s1, char *s2, 
  51.                  unsigned back, unsigned fore);
  52.  
  53.  
  54. /* Cursor Control Variables and Function Prototypes */
  55. extern unsigned ShortCursor, MidCursor, TallCursor, OldCursor, NoCursor;
  56. void initCursor(void), SetCursor(unsigned shape);
  57.  
  58. /* main function */
  59. void main(int argc, char *argv[])
  60. {
  61.   char s[80];
  62.   struct address *RecordBuffer;
  63.  
  64.   gettextinfo(&textinfo);
  65.  
  66.   FirstRecord = Last = NULL;
  67.   *FileName = NULL;
  68.  
  69.   textmode(3);
  70.   textcolor(YELLOW);
  71.  
  72.   initCursor();
  73.   SetCursor(NoCursor);
  74.  
  75.   /* if command line arguements... */
  76.   if (argc >= 2)
  77.   {
  78.     clrscr();
  79.     strcpy(FileName, argv[1]);
  80.     open_file();
  81.   }
  82.  
  83.   /* draw box */
  84.   charbox();
  85.  
  86.   /* shrink window */
  87.   window(2, 2, 79, 24);
  88.  
  89.   /* main program loop */
  90.   for (;;)
  91.   {
  92.     switch (menu_select())
  93.     {
  94.       case 1:     enter();  /* enter record            */
  95.         break;
  96.       case 2:     delete(); /* remove an address       */
  97.         break;
  98.       case 3:     labels();        /* write list as labels */
  99.         break;
  100.       case 4:     search(); /* find a record      */
  101.         break;
  102.       case 5:     edit();
  103.         break;
  104.       case 6:     save();        /* save list to disk       */
  105.         break;
  106.       case 7:     load();        /* load from disk     */
  107.         break;
  108.       case 8:     browse();      /* allow user to browse through list */
  109.         break;
  110.       case 9:     spawnit();
  111.         break;
  112.       case 10:
  113.         if (FileName[0])
  114.         {
  115.           unsigned answer;
  116.             
  117.           cls();
  118.           go_xy(1, 2);
  119.           cprintf("%s loaded.  Save before exiting? (Y/N)\n", FileName);
  120.           go_xy(2, 2);
  121.           cputs("(Press ESCAPE to return to program)");
  122.           textattr(YELLOW | BLINK | LIGHTGRAY);
  123.           go_xy(3, 2);
  124.           cprintf("WARNING -- Current data will be lost if not saved!");
  125.           answer = getch();
  126.           if (answer == ESC)
  127.           {
  128.             textbackground(BLACK);
  129.             textcolor(YELLOW);
  130.             break;
  131.           } 
  132.           if ((toupper(answer) == 'Y'))
  133.           {
  134.             clrscr();
  135.             textbackground(BLACK);
  136.             textcolor(YELLOW);
  137.             WriteFile();
  138.           }
  139.         }
  140.         textattr(textinfo.attribute);
  141.         window(1, 1, 80, 25);
  142.         SetCursor(OldCursor);
  143.         clrscr();
  144.         exit(0);
  145.     }
  146.   }
  147. }
  148.  
  149. /* Enter names and addresses */
  150. void enter()
  151. {
  152.   struct address *store_record();
  153.   struct address *enteree;
  154.   unsigned RecordIndex = 6;
  155.   unsigned max = 0;
  156.   unsigned begin = 31;
  157.   char *s;
  158.  
  159.   cls();
  160.  
  161.   enteree = (struct address *) calloc(1, sizeof(struct address));
  162.   if (!enteree)
  163.   {
  164.     cls();
  165.     puts("out of memory");
  166.     return;
  167.   }
  168.  
  169.   display(&Dummy, "Add Record Mode", 
  170.           "Enter a record, press any key to continue", YELLOW, LIGHTGRAY);
  171.   SetCursor(TallCursor);
  172.  
  173.   while ( (RecordIndex <= 18) && (RecordIndex >= 4) )
  174.   {
  175.     if (RecordIndex == 4) RecordIndex += 2;
  176.  
  177.     switch (RecordIndex)
  178.     {
  179.       case  4:
  180.         break;
  181.       case 6:
  182.         s = enteree->FirstName;
  183.         max = 14;
  184.           break;
  185.       case 8:
  186.         s = enteree->LastName;
  187.           max = 14;
  188.           break;
  189.       case 10:
  190.         s = enteree->Street;
  191.           max = 39;
  192.           break;
  193.       case 12:
  194.           s = enteree->City;
  195.           max = 19;
  196.           break;
  197.       case 14:
  198.         s = enteree->State;
  199.           max = 2;
  200.           break;
  201.       case 16:
  202.           s = enteree->Zip;
  203.           max = 9;
  204.           break;
  205.       case 18:
  206.           s = enteree->Phone;
  207.           max = 14;
  208.           break;
  209.     }
  210.     gotoxy(begin, RecordIndex);
  211.     RecordIndex = edit_field(begin, RecordIndex, s, max, LIGHTGRAY, YELLOW);
  212.     
  213.  
  214.     if ((RecordIndex == 8) && (*s == 0)) return;
  215.  
  216.   } /* while loop for input of fields */
  217.   cls();
  218.   FirstRecord = store_record(enteree, FirstRecord);
  219. }     
  220.  
  221. /* edit a record */
  222. void edit(void)
  223. {
  224.   int RecordIndex = 6, max = 0;
  225.   int begin = 31;
  226.   char sfirst[20], slast[20];
  227.   struct address *editee, *find();
  228.   register int loop;
  229.   char *s;
  230.  
  231.   /* initialize input strings */
  232.   for (loop = 0; loop < 20; loop++)
  233.   {
  234.     sfirst[loop] = '\0';
  235.     slast[loop] = '\0';
  236.   }
  237.  
  238.   cls();
  239.   SetCursor(TallCursor);
  240.   go_xy(1, 2);
  241.   cputs(FirstPrompt);
  242.   edit_field(27, 1, sfirst, 15, LIGHTGRAY, YELLOW);
  243.   go_xy(2, 2);
  244.   cputs(LastPrompt);
  245.   edit_field(27, 2, slast, 15, LIGHTGRAY, YELLOW);
  246.   SetCursor(NoCursor);
  247.  
  248.   cls();
  249.  
  250.   editee = find(sfirst, slast);
  251.  
  252.   if (!editee) return;
  253.  
  254.   display(editee, "Edit Record Mode", "Change Record - <enter> to continue",
  255.           YELLOW, LIGHTGRAY);
  256.  
  257.   SetCursor(TallCursor);
  258.   while ( (RecordIndex <= 18) && (RecordIndex >= 4) )
  259.   {
  260.     if (RecordIndex == 4) RecordIndex += 2;
  261.  
  262.     switch (RecordIndex)
  263.     {
  264.       case  4:
  265.           break;
  266.  
  267.       case 6:
  268.           s = editee->FirstName;
  269.           max = 14;
  270.           break;
  271.  
  272.       case 8:
  273.           s = editee->LastName;
  274.           max = 14;
  275.           break;
  276.  
  277.       case 10:
  278.           s = editee->Street;
  279.           max = 39;
  280.           break;
  281.  
  282.       case 12:
  283.           s = editee->City;
  284.           max = 19;
  285.           break;
  286.  
  287.       case 14:
  288.           s = editee->State;
  289.           max = 2;
  290.           break;
  291.  
  292.       case 16:
  293.           s = editee->Zip;
  294.           max = 9;
  295.           break;
  296.  
  297.       case 18:
  298.           s = editee->Phone;
  299.           max = 14;
  300.           break;
  301.     }
  302.     gotoxy(begin, RecordIndex);
  303.     RecordIndex = edit_field(begin, RecordIndex, s, max, LIGHTGRAY, YELLOW);
  304.   }  /* while RecordIndex OK & editee  */
  305.  
  306.   SetCursor(NoCursor);
  307.  
  308.   /* put revised record into sorted order in list */
  309.  
  310.   if(FirstRecord == editee)
  311.   {
  312.     FirstRecord = editee->Next;
  313.     if (FirstRecord) FirstRecord->Prior = NULL;
  314.     else Last = NULL;
  315.   }
  316.   else
  317.   {
  318.     editee->Prior->Next = editee->Next;
  319.     if (editee != Last)
  320.       editee->Next->Prior = editee->Prior;
  321.     else
  322.      Last = editee->Prior;
  323.   }
  324.  
  325.   FirstRecord = store_record(editee, FirstRecord);
  326.  
  327. }  /* edit */
  328.  
  329. unsigned edit_field(const unsigned col, unsigned row,
  330.             char *s, unsigned max, unsigned back, unsigned fore)
  331. /* A field edit routine that that returns an interger
  332.    that is incremented if the CR, down arrow, or page down is hit,
  333.    and is decremented if the up arrow of page up is hit.  This
  334.    interger can be used to indicate field numbers.  The length of the
  335.    string passed to edit must have at least as many characters allocated
  336.    to it as the number "max"  */
  337. {
  338.  unsigned character, end = (strlen(s) + col);
  339.  unsigned pos = col, Index, IsSpace;
  340.  const char Space = ' ';
  341.  struct text_info textinfo;
  342.  
  343.  gettextinfo(&textinfo);
  344.  max += col;
  345.  
  346.  textbackground(back);
  347.  textcolor(fore);
  348.  gotoxy(col, row);
  349.  field(s, (max+1) - col);
  350.  do
  351.  
  352.  {
  353.   switch(character = getkey())
  354.   {
  355.    case HOME :
  356.     pos = col;
  357.     gotoxy(col, row);
  358.     break;
  359.    case END :
  360.     pos = end;
  361.     gotoxy(end, row);
  362.     break;
  363.    case LEFT :
  364.     if (pos > col)
  365.     {
  366.      pos--;
  367.      gotoxy(pos, row);
  368.     }
  369.     else
  370.     {
  371.       Beep();
  372.     }
  373.     break;
  374.    case RIGHT :
  375.     if (pos < end)
  376.     {
  377.      pos++;
  378.      gotoxy(pos, row);
  379.     }
  380.     else
  381.     {
  382.      Beep();
  383.     }
  384.     break;
  385.    case BS :
  386.     if (pos == col)
  387.     {
  388.       Beep();
  389.       break;
  390.     }
  391.     pos--;
  392.     for (Index = pos - col; Index <= end - col; Index++)
  393.     {
  394.      s[Index] = s[Index + 1];
  395.     }
  396.     gotoxy(col, row);
  397.     cputs(s);
  398.     putchar(Space);
  399.     end--;
  400.     gotoxy(pos, row);
  401.     break;
  402.    case DEL :
  403.     if (pos >= end)
  404.     {
  405.       Beep();
  406.       break;
  407.     }
  408.     end--;
  409.     for(Index = pos - col; Index <= end - col; Index++)
  410.      s[Index] = s[Index+1];
  411.     gotoxy(col, row);
  412.     cputs(s);
  413.     putchar(Space);
  414.     gotoxy(pos, row);
  415.     break;
  416.    case PGUP :
  417.    case UP :
  418.     if (row >= 1)
  419.     {
  420.       textattr(textinfo.attribute);
  421.       return(row - 2);
  422.     }
  423.     break;
  424.    case PGDN :
  425.    case CR :
  426.    case DOWN :
  427.     textattr(textinfo.attribute);
  428.     return (row + 2);
  429.    case ESC :
  430.     pos = col;
  431.     gotoxy(col, row);
  432.     break;
  433.    case CTRLLEFT :
  434.     if (pos == col)
  435.     {
  436.       Beep();
  437.       break;
  438.     }
  439.     IsSpace = FALSE;
  440.     Index = (pos - col - 2);
  441.     if (Index <= 0)
  442.     {
  443.      gotoxy(col, row);
  444.      Index = 0;
  445.      pos = col;
  446.      break;
  447.     }
  448.     while ((!IsSpace) && (Index != 0))
  449.     {
  450.      while (!IsSpace)
  451.      {
  452.       if ((s[Index] == ' ') | (s[Index] == 0))
  453.     IsSpace = TRUE;
  454.       Index--;
  455.       if (Index <= 0)
  456.       {
  457.     pos = col;
  458.     Index = 0;
  459.     gotoxy(col, row);
  460.     break;
  461.       }
  462.      }
  463.      if (Index == 0)
  464.       break;
  465.      Index += 3;
  466.      pos = Index + col -1;
  467.      gotoxy(pos, row);
  468.     }
  469.     break;
  470.    case CTRLRIGHT :
  471.     if (pos >= end)
  472.     {
  473.       Beep();
  474.       break;
  475.     }
  476.     IsSpace = FALSE;
  477.     Index = (pos - col);
  478.     while ((!IsSpace) && (pos < end))
  479.     {
  480.      if (s[Index] == ' ')
  481.      {
  482.        IsSpace = TRUE;
  483.        Index++;
  484.      }
  485.      while (!IsSpace)
  486.      {
  487.       if ((s[Index] == ' ') | (s[Index] == NULL))
  488.     IsSpace = TRUE;
  489.       if (s[Index] == NULL) Index--;
  490.       Index++;
  491.      }
  492.      Index++;
  493.      pos = Index + col -1;
  494.      gotoxy(pos, row);
  495.     }
  496.     break;
  497.    default  :
  498.     if ((character >= ' ') && (character <= '~'))
  499.     {
  500.      if (end >= max)
  501.      {
  502.       Beep();
  503.       break;
  504.      }
  505.      for (Index = end - col + 1; Index > pos - col; Index--)
  506.        s[Index] = s[Index - 1];
  507.      s[Index] = ((char)character);
  508.      if (strlen(s) <= 1)
  509.      s[Index + 1] = NULL;
  510.      gotoxy(col, row);
  511.      end++;
  512.      s[end-col] = NULL;
  513.      cputs(s);
  514.      pos++;
  515.      gotoxy(pos, row);
  516.      break;
  517.     }  /* if */
  518.   }  /* switch */
  519.   s[end-col] = NULL;
  520.  }  while (1);  /* do */
  521. }  /* editstring */
  522.  
  523.  
  524. /* Create a doubly linked list in sorted order.  A pointer
  525.    to the first element is returned */
  526.  
  527. struct address *store_record(struct address *insertee,       /* new record */
  528.                           struct address *first)     /* store in sorted order */
  529. {
  530.   struct address *old, *next_to_compare;
  531.   char *Spaces = "                              ";
  532.   char insert_buffer[32];
  533.   char next_buffer[32];
  534.   char *insert_first = &insert_buffer[16];
  535.   char *next_first = &next_buffer[16];
  536.   unsigned Index;
  537.  
  538.   if (Last == NULL)              /* first element in list */
  539.   {
  540.     insertee->Next  = NULL;
  541.     insertee->Prior = NULL;
  542.     Last   = insertee;
  543.     return insertee;
  544.   }
  545.   next_to_compare = first;                  /* start at top of list */
  546.   old = NULL;
  547.   
  548.   /* set up last name, first name Indexes */
  549.   
  550.   strcpy(insert_buffer, Spaces);
  551.   strcpy(next_buffer, Spaces);
  552.   
  553.   Index = 0;
  554.   
  555.   while (insertee->LastName[Index])
  556.   {
  557.     insert_buffer[Index] = insertee->LastName[Index];
  558.     Index++;
  559.   }
  560.          
  561.   Index = 0;
  562.   
  563.   while (next_to_compare->LastName[Index])
  564.   {
  565.     next_buffer[Index] = next_to_compare->LastName[Index];
  566.     Index++;
  567.   }
  568.   
  569.   strcpy(insert_first, insertee->FirstName);
  570.   strcpy(next_first, next_to_compare->FirstName); 
  571.   
  572.   while (next_to_compare)
  573.   {
  574.     if (strcmpi(next_buffer, insert_buffer) < 0)
  575.     {
  576.       old = next_to_compare;
  577.       next_to_compare = next_to_compare->Next;
  578.  
  579.       strcpy(insert_buffer, Spaces);
  580.       strcpy(next_buffer, Spaces);
  581.   
  582.       Index = 0;
  583.   
  584.       while (insertee->LastName[Index])
  585.       {
  586.         insert_buffer[Index] = insertee->LastName[Index];
  587.         Index++;
  588.       }
  589.          
  590.       Index = 0;
  591.   
  592.       while (next_to_compare->LastName[Index])
  593.       {
  594.         next_buffer[Index] = next_to_compare->LastName[Index];
  595.         Index++;
  596.       }
  597.   
  598.       strcpy(insert_first, insertee->FirstName);
  599.       strcpy(next_first, next_to_compare->FirstName); 
  600.     }
  601.     else
  602.     {
  603.       if (next_to_compare->Prior)
  604.       {
  605.     next_to_compare->Prior->Next = insertee;
  606.     insertee->Next = next_to_compare;
  607.     insertee->Prior = next_to_compare->Prior;
  608.     next_to_compare->Prior = insertee;
  609.     return first;
  610.       }
  611.       insertee->Next = next_to_compare;          /* new first element     */
  612.       insertee->Prior = NULL;
  613.       next_to_compare->Prior = insertee;
  614.       return insertee;
  615.     }
  616.   }
  617.   old->Next = insertee;       /* put on end       */
  618.   insertee->Next = NULL;
  619.   insertee->Prior = old;
  620.   Last = insertee;
  621.   return FirstRecord;
  622. }
  623.  
  624. /* remove an element from the list */
  625. delete()
  626. {
  627.   struct address *RecordBuffer, *find();
  628.   char slast[15], sfirst[15];
  629.   char answer;
  630.   unsigned loop;
  631.  
  632.   for (loop = 0; loop < 15; loop++)
  633.   {
  634.     sfirst[loop] = '\0';
  635.     slast[loop] = '\0';
  636.   }
  637.  
  638.   cls();
  639.   SetCursor(TallCursor);
  640.   go_xy(1, 2);
  641.   cputs(FirstPrompt);
  642.   edit_field(27, 1, sfirst, 15, LIGHTGRAY, YELLOW);
  643.   go_xy(2, 2);
  644.   cputs(LastPrompt);
  645.   edit_field(27, 2, slast, 15, LIGHTGRAY, YELLOW);
  646.   SetCursor(NoCursor);
  647.   
  648.   RecordBuffer = find(sfirst, slast);
  649.  
  650.   if (RecordBuffer)
  651.   {
  652.     clrscr();
  653.     SetCursor(TallCursor);
  654.     answer = display(RecordBuffer, "Delete Record Mode", "Ok to delete? (Y/N)", 
  655.                      YELLOW, LIGHTGRAY);
  656.     SetCursor(NoCursor);
  657.     
  658.     if (toupper(answer) == 'Y')
  659.     {
  660.       if(FirstRecord == RecordBuffer)
  661.       {
  662.     FirstRecord = RecordBuffer->Next;
  663.     if (FirstRecord) 
  664.       FirstRecord->Prior = NULL;
  665.     else 
  666.       Last = NULL;
  667.     cls();
  668.     go_xy(1, 2);
  669.     cprintf("Record deleted");
  670.     getch();
  671.       }
  672.       else
  673.       {
  674.     RecordBuffer->Prior->Next = RecordBuffer->Next;
  675.     if (RecordBuffer != Last)
  676.       RecordBuffer->Next->Prior = RecordBuffer->Prior;
  677.     else
  678.     Last = RecordBuffer->Prior;
  679.     cls();
  680.     go_xy(1, 2);
  681.     cprintf("Record deleted");
  682.     getch();
  683.       }
  684.     }
  685.   }
  686.   else
  687.   {
  688.     clrscr();
  689.     go_xy(1, 2);
  690.     cprintf("record not found, press <CR> to continue...");
  691.     getch();
  692.   }
  693. }
  694.  
  695. /* find the address given the name */
  696. struct address *find(char *sfirst, char *slast)
  697. {
  698.   struct address *RecordBuffer;
  699.   RecordBuffer = FirstRecord;
  700.   while (RecordBuffer)
  701.   {
  702.     if (!strcmpi(slast, RecordBuffer->LastName))
  703.     {
  704.       if (!strcmpi(sfirst, RecordBuffer->FirstName))
  705.       return RecordBuffer;
  706.     }
  707.     RecordBuffer = RecordBuffer->Next;           /* get next record */
  708.   }
  709.   cls();
  710.   return NULL;                   /* not found   */
  711. }
  712.  
  713. /* allow the user to browse the list, and
  714.    edit a record if he or she wishes
  715. */
  716. void browse(void)
  717. {
  718.   unsigned struct address *RecordBuffer, *InfoBuffer;
  719.   unsigned ans = 0, max = 0;
  720.   char *s;
  721.   unsigned startflag = FALSE;
  722.  
  723.   RecordBuffer = FirstRecord;
  724.   cls();
  725.  
  726.   while (RecordBuffer)
  727.   {
  728.     unsigned RecordIndex = 6;
  729.     const unsigned begin = 31;
  730.  
  731.     if (startflag)
  732.     {
  733.       ans = getkey();
  734.       if ((ans == UP) || (ans == PGUP))
  735.            Beep();
  736.       startflag = FALSE;
  737.     }
  738.     else
  739.     {
  740.       ans = display(RecordBuffer, "Browse List Mode", 
  741.                    "PGUP=prior ESC=abort CR=next F10=edit", YELLOW, LIGHTGRAY);
  742.     }
  743.     
  744.     switch(ans)
  745.     {
  746.       case PGUP:
  747.       case UP:
  748.         if (RecordBuffer->Prior == NULL) 
  749.         {
  750.           startflag = TRUE;
  751.           break;
  752.         }
  753.         RecordBuffer = RecordBuffer->Prior;
  754.         break;
  755.         
  756.       /* edit a record */
  757.       case F10:
  758.         InfoBuffer = RecordBuffer;
  759.         RecordBuffer = InfoBuffer->Next;
  760.         SetCursor(TallCursor);
  761.         while ( (RecordIndex <= 18) && (RecordIndex >= 4) )
  762.         {
  763.           if (RecordIndex == 4) RecordIndex += 2;
  764.  
  765.           switch (RecordIndex)
  766.           {
  767.             case  4:
  768.               break;
  769.  
  770.             case 6:
  771.               s = InfoBuffer->FirstName;
  772.               max = 14;
  773.                 break;
  774.  
  775.             case 8:
  776.               s = InfoBuffer->LastName;
  777.               max = 14;
  778.                 break;
  779.  
  780.             case 10:
  781.               s = InfoBuffer->Street;
  782.               max = 39;
  783.               break;
  784.  
  785.             case 12:
  786.                 s = InfoBuffer->City;
  787.               max = 19;
  788.               break;
  789.  
  790.             case 14:
  791.               s = InfoBuffer->State;
  792.               max = 2;
  793.               break;
  794.  
  795.             case 16:
  796.               s = InfoBuffer->Zip;
  797.               max = 9;
  798.               break;
  799.  
  800.             case 18:
  801.               s = InfoBuffer->Phone;
  802.               max = 14;
  803.               break;
  804.            }
  805.            gotoxy(begin, RecordIndex);
  806.            RecordIndex = edit_field(begin, RecordIndex, s, max, 
  807.                                     LIGHTGRAY, YELLOW);
  808.  
  809.         } /* while loop for input of fields */
  810.         SetCursor(NoCursor);
  811.         /* put revised record into sorted order in list */
  812.  
  813.         if(FirstRecord == InfoBuffer)
  814.         {
  815.           FirstRecord = InfoBuffer->Next;
  816.           if (FirstRecord) FirstRecord->Prior = NULL;
  817.           else Last = NULL;
  818.         }
  819.         else
  820.         {
  821.           InfoBuffer->Prior->Next = InfoBuffer->Next;
  822.           if (InfoBuffer != Last)
  823.             InfoBuffer->Next->Prior = InfoBuffer->Prior;
  824.           else
  825.             Last = InfoBuffer->Prior;
  826.         }
  827.         
  828.         FirstRecord = store_record(InfoBuffer, FirstRecord);
  829.  
  830.     break;
  831.       case ESC:
  832.         return;
  833.       default:
  834.         RecordBuffer = RecordBuffer->Next;
  835.         break;
  836.     }
  837.   }
  838. }
  839.  
  840. /* put the list to a file in label format */
  841. void labels(void)
  842. {
  843.   FILE *labels;
  844.   struct address *RecordBuffer;
  845.  
  846.   RecordBuffer = FirstRecord;
  847.   
  848.   if ((labels = fopen("label.txt", "wt")) == NULL)
  849.   {
  850.     cls();
  851.     go_xy(1, 2);
  852.     puts("Unable to open file \"label.txt\"");
  853.     return;
  854.   }
  855.  
  856.   while(RecordBuffer)
  857.   {
  858.     fprintf(labels, "%s ", RecordBuffer->FirstName);
  859.     fprintf(labels, "%s\n", RecordBuffer->LastName);
  860.     fprintf(labels, "%s\n", RecordBuffer->Street);
  861.     fprintf(labels, "%s, ", RecordBuffer->City);
  862.     fprintf(labels, "%s   ", RecordBuffer->State);
  863.     fprintf(labels, "%s\n\n", RecordBuffer->Zip);
  864.     RecordBuffer = RecordBuffer->Next;
  865.   }
  866.   fclose(labels);
  867.   cls();
  868.   go_xy(1, 2);
  869.   cputs("Labels sent to file \"label.txt\"");
  870.   go_xy(2, 2);
  871.   cputs("Press any key to continue...");
  872.   getch();
  873. }
  874.  
  875.  
  876. /* display an address */
  877. unsigned display(struct address *RecordBuffer, char *s1, char *s2, 
  878.                  unsigned fore, unsigned back)
  879. {
  880.   int loop;
  881.   unsigned answer;
  882.   unsigned s1y = ((80 - strlen(s1))/2);
  883.   unsigned s2y = ((80 - strlen(s2))/2);
  884.  
  885.   struct text_info textinfo;
  886.  
  887.   gettextinfo(&textinfo);
  888.  
  889.   gotoxy(18, 6);
  890.   cprintf("first name:  ");
  891.   gotoxy(18, 8);
  892.   cprintf(" last name:  ");
  893.   gotoxy(18, 10);
  894.   cprintf("    street:  ");
  895.   gotoxy(18, 12);
  896.   cprintf("      city:  ");
  897.   gotoxy(18, 14);
  898.   cprintf("     state:  ");
  899.   gotoxy(18, 16);
  900.   cprintf("       zip:  ");
  901.   gotoxy(18, 18);
  902.   cprintf("     phone:  ");
  903.  
  904.   textbackground(back);
  905.   textcolor(fore);
  906.  
  907.   gotoxy(31, 6);
  908.   field(RecordBuffer->FirstName, 15);
  909.   gotoxy(31, 8);
  910.   field(RecordBuffer->LastName, 15);
  911.   gotoxy(31, 10);
  912.   field(RecordBuffer->Street, 41);
  913.   gotoxy(31, 12);
  914.   field(RecordBuffer->City, 20);
  915.   gotoxy(31, 14);
  916.   field(RecordBuffer->State, 3);
  917.   gotoxy(31, 16);
  918.   field(RecordBuffer->Zip, 10);
  919.   gotoxy(31, 18);
  920.   field(RecordBuffer->Phone, 15);
  921.  
  922.   textattr(textinfo.attribute);
  923.  
  924.   go_xy(3, s1y);
  925.  
  926.   cputs(s1);
  927.   
  928.   go_xy(21, s2y);
  929.  
  930.   cputs(s2);
  931.  
  932.   answer = getkey();
  933.  
  934.   return answer;
  935. }
  936.  
  937. /* search for an address given the name */
  938. void search()
  939. {
  940.   char fname[15];
  941.   char lname[15];
  942.   struct address *RecordBuffer, *find();
  943.   unsigned loop;
  944.   
  945.   cls();
  946.   for (loop = 0; loop < 15; loop++)
  947.   {
  948.     fname[loop] = '\0';
  949.     lname[loop] = '\0';
  950.   };
  951.   SetCursor(TallCursor);
  952.   go_xy(1, 2);
  953.   cputs(FirstPrompt);
  954.   edit_field(27, 1, fname, 15, LIGHTGRAY, YELLOW);
  955.   go_xy(2, 2);
  956.   cputs(LastPrompt);
  957.   edit_field(27, 2, lname, 15, LIGHTGRAY, YELLOW);
  958.   SetCursor(NoCursor);
  959.  
  960.   if (!((RecordBuffer=find(fname, lname))!=0))
  961.   {
  962.     cls();
  963.     go_xy(1, 2);
  964.     cputs("name not found, press <CR> to continue...");
  965.     getch();
  966.   }
  967.   else
  968.   {
  969.     cls();
  970.     display(RecordBuffer, "Display Record Mode", "Press any key to continue...", 
  971.             YELLOW, LIGHTGRAY);
  972.   }
  973. }
  974.  
  975. void save()
  976. {
  977.   cls();
  978.   go_xy(1, 2);
  979.   cputs("Enter name of file:  ");
  980.   SetCursor(TallCursor);
  981.   inputs(FileName, 15);
  982.   SetCursor(NoCursor);
  983.   WriteFile();
  984. }
  985.  
  986. /* save the mailing list to a disk file */
  987. void WriteFile()
  988. {
  989.   register int t;
  990.   struct address *RecordBuffer;
  991.   FILE *fp;
  992.   if ((fp = fopen(FileName, "wb")) == NULL)
  993.   {
  994.     cls();
  995.     go_xy(1, 2);
  996.     cprintf("cannot save file %s", FileName);
  997.     getch();
  998.     return;
  999.   }
  1000.   go_xy(2, 2);
  1001.   cputs("saving file");
  1002.   RecordBuffer = FirstRecord;
  1003.   while (RecordBuffer)
  1004.   {
  1005.     fwrite(RecordBuffer, sizeof(struct address), 1, fp);
  1006.     RecordBuffer = RecordBuffer->Next;           /* get next address */
  1007.   }
  1008.   fclose(fp);
  1009.   go_xy(3, 2);
  1010.   cputs("file saved");
  1011.   go_xy(4, 2);
  1012.   cputs("press <CR> to continue...");
  1013.   getch();
  1014. }
  1015.  
  1016. /* load an address file from disk */
  1017. void load()
  1018. {
  1019.   char answer;
  1020.  
  1021.   cls();
  1022.  
  1023.   if (FileName[0] > 1)
  1024.   {
  1025.     cls();
  1026.     go_xy(1, 2);
  1027.     cprintf("%s already loaded.  Load new file? (Y/N)\n", FileName);
  1028.     textattr(YELLOW | BLINK | LIGHTGRAY);
  1029.     go_xy(2, 2);
  1030.     cprintf("WARNING -- Current data will be lost if not saved!");
  1031.     textbackground(BLACK);
  1032.     textcolor(YELLOW);
  1033.     answer = getch();
  1034.     if (!(toupper(answer) == 'Y'))
  1035.       return;
  1036.   }
  1037.   cls();
  1038.   go_xy(1, 2);
  1039.   SetCursor(TallCursor);
  1040.   cputs("File name:  ");
  1041.   inputs(FileName, 15);
  1042.   SetCursor(NoCursor);
  1043.   cls();
  1044.   open_file();
  1045. }
  1046.  
  1047. /* actually open the file */
  1048. void open_file()
  1049. {
  1050.   register int t;
  1051.   struct address *RecordBuffer, *temp = NULL;
  1052.   FILE *fp;
  1053.  
  1054.   if((fp = fopen(FileName, "rb")) == NULL)
  1055.   {
  1056.     cls();
  1057.     *FileName = NULL;
  1058.     go_xy(1, 2);
  1059.     cprintf("cannot open file\n");
  1060.     go_xy(2, 2);
  1061.     cputs("press <CR> to continue...");
  1062.     getch();
  1063.     FileName[1] = 0;
  1064.     return;
  1065.   }
  1066.  
  1067.   while (FirstRecord)
  1068.   {
  1069.     RecordBuffer = FirstRecord->Next;
  1070.     free(RecordBuffer);
  1071.     FirstRecord = RecordBuffer;
  1072.   }
  1073.  
  1074.   FirstRecord = (struct address *) calloc(1, sizeof(struct address));
  1075.   if (!FirstRecord)
  1076.   {
  1077.     cls();
  1078.     go_xy(1, 2);
  1079.     cputs("Out of memory");
  1080.     go_xy(2, 2);
  1081.     cputs("press <CR> to continue...");
  1082.     return;
  1083.   }
  1084.   RecordBuffer = FirstRecord;
  1085.   count = 0;
  1086.   while (!feof(fp))
  1087.   {
  1088.     if (1 != fread(RecordBuffer, sizeof(struct address), 1, fp)) break;
  1089.     /* get memory for next */
  1090.     RecordBuffer->Next = (struct address *) calloc(1, sizeof(struct address));
  1091.     ++count;
  1092.     if (!RecordBuffer->Next)
  1093.     {
  1094.       printf("out of memory\n");
  1095.       return;
  1096.     }
  1097.     RecordBuffer->Prior = temp;
  1098.     temp = RecordBuffer;
  1099.     RecordBuffer = RecordBuffer->Next;
  1100.   }
  1101.   temp->Next = NULL;             /* last entry */
  1102.   Last = temp;
  1103.  
  1104.   FirstRecord->Prior = NULL;
  1105.   fclose(fp);
  1106.   go_xy(1, 2);
  1107.   cputs("File loaded....");
  1108.   go_xy(2, 2);
  1109.   cprintf("Total of %d records in file.", count);
  1110.   go_xy(3, 2);
  1111.   cputs("press any key to continue...");
  1112.   getch();
  1113.   return;
  1114. }
  1115.  
  1116. /* shell out to DOS */
  1117. void spawnit(void)
  1118. {
  1119.   textattr(textinfo.attribute);
  1120.   window(1, 1, 80, 25);
  1121.   SetCursor(ShortCursor);
  1122.   clrscr();
  1123.   puts("Type \"EXIT\" to return to program.");
  1124.   
  1125.   spawnl(P_WAIT, "c:\\command", NULL);
  1126.   
  1127.   clrscr();
  1128.   SetCursor(NoCursor);
  1129.   textcolor(YELLOW);
  1130.   charbox();
  1131.   window(2, 2, 79, 24);
  1132. }
  1133.